//
// Created by alexander on 1/25/19.
//
#include "convertMp4ToFlv.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/timestamp.h>
#include </home/alexander/CLionProjects/FFmpegLogTest/log/log.h>
#include </home/alexander/CLionProjects/FFmpegLogTest/util/util.h>

void prientPacketInfo(const AVFormatContext *avFormatContext, const AVPacket *avPacket, const char *tag)
{
    AVRational *time_base = &avFormatContext->streams[avPacket->stream_index]->time_base;

    printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s stream_index:%d\n",
            tag,
            av_ts2str(avPacket->pts), av_ts2timestr(avPacket->pts, time_base),
            av_ts2str(avPacket->dts), av_ts2timestr(avPacket->dts, time_base),
            av_ts2str(avPacket->duration), av_ts2timestr(avPacket->duration, time_base),
            avPacket->stream_index);
}

int coverMp4ToFlv(const char *inputFileName, const char *outputFileName)
{
    AVOutputFormat *avOutputFormat = NULL;
    AVFormatContext *inputAVFormatContext = NULL, *outputAVFormatContext = NULL;
    /**这样定义的话，这是一个实例，有自己的存储空间*/
    AVPacket avPacket;
    int returnCode, i;
    int streamIndex = 0;
    int *streamMapping = NULL;
    int streamMappingSize = 0;

    av_register_all();

    returnCode = avformat_open_input(&inputAVFormatContext, inputFileName, 0, 0);
    if(returnCode < 0)
    {
        logError("Counld not open input file '%s'\n", inputFileName);
        goto faile;
    }

    returnCode = avformat_find_stream_info(inputAVFormatContext, 0);
    if(returnCode < 0)
    {
        logError("Failed to retrieve input stream info", "");
        goto faile;
    }

    av_dump_format(inputAVFormatContext, 0, inputFileName, 0);

    avformat_alloc_output_context2(&outputAVFormatContext, NULL, NULL, outputFileName);
    if(!outputAVFormatContext)
    {
        logError("Counld not create out context!\n", "");
        returnCode = AVERROR_UNKNOWN;
        goto faile;
    }

    streamMappingSize = inputAVFormatContext->nb_streams;
    streamMapping = av_mallocz_array(streamMappingSize, sizeof(*streamMapping));
    if(!streamMapping)
    {
        returnCode = AVERROR(ENOMEM);
        goto faile;
    }

    avOutputFormat = outputAVFormatContext->oformat;

    /**复制我们需要的每一路流的结构；并复制所有参数*/
    for (i = 0; i < inputAVFormatContext->nb_streams; ++i)
    {
        AVStream *outAVStream;
        AVStream *inAVStream = inputAVFormatContext->streams[i];
        AVCodecParameters *inAVCodeParameters = inAVStream->codecpar;

        if(inAVCodeParameters->codec_type != AVMEDIA_TYPE_AUDIO &&
        inAVCodeParameters->codec_type != AVMEDIA_TYPE_VIDEO &&
        inAVCodeParameters->codec_type != AVMEDIA_TYPE_SUBTITLE)
        {
            streamMapping[i] = -1;
            continue;
        }

        streamMapping[i] = streamIndex++;

        outAVStream = avformat_new_stream(outputAVFormatContext, NULL);
        if(!outAVStream)
        {
            logError("Failed allocating output stream！\n", "");
            returnCode = AVERROR_UNKNOWN;
            goto faile;
        }

        returnCode = avcodec_parameters_copy(outAVStream->codecpar, inAVCodeParameters);
        if(returnCode < 0)
        {
            logError("Failed to copy codec parameters!\n", "");
            goto faile;
        }
        outAVStream->codecpar->codec_tag = 0;
    }
    av_dump_format(outputAVFormatContext, 0, outputFileName, 1);

    if(!(avOutputFormat->flags & AVFMT_NOFILE))
    {
        returnCode = avio_open(&outputAVFormatContext->pb, outputFileName, AVIO_FLAG_WRITE);
        if(returnCode < 0)
        {
            logError("Counld not open output file '%s'!", outputFileName);
            goto faile;
        }
    }

    /**写入文件头*/
    returnCode = avformat_write_header(outputAVFormatContext, NULL);
    if(returnCode < 0)
    {
        logError("Error eccurred when opening output file!\n", "");
        goto faile;
    }

    /**写入数据*/
    while (1)
    {
        AVStream *inAVStream, *outAVStream;

        /**读初一帧数据放入AcPacket中*/
        returnCode = av_read_frame(inputAVFormatContext, &avPacket);
        if(returnCode < 0)
        {
            break;
        }

        inAVStream = inputAVFormatContext->streams[avPacket.stream_index];
        if(avPacket.stream_index >= streamMappingSize || streamMapping[avPacket.stream_index] < 0)
        {
            av_packet_unref(&avPacket);
            continue;
        }

        avPacket.stream_index = streamMapping[avPacket.stream_index];
        outAVStream = outputAVFormatContext->streams[avPacket.stream_index];
        prientPacketInfo(inputAVFormatContext, &avPacket, "in");

        /**copy packet   进行时间刻度的转换*/
        avPacket.pts = av_rescale_q_rnd(avPacket.pts, inAVStream->time_base, outAVStream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        avPacket.dts = av_rescale_q_rnd(avPacket.dts, inAVStream->time_base, outAVStream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        avPacket.duration = av_rescale_q(avPacket.duration, inAVStream->time_base, outAVStream->time_base);
        avPacket.pos = 1;
        prientPacketInfo(outputAVFormatContext, &avPacket, "out");

        /**把处理过后的数据写入输出文件中*/
        returnCode = av_interleaved_write_frame(outputAVFormatContext, &avPacket);
        if(returnCode < 0)
        {
            logError("Error muxing packet\n", "");
            break;
        }
        /**释放掉无用的数据帧*/
        av_packet_unref(&avPacket);
    }

    /**写入尾部数据*/
    av_write_trailer(outputAVFormatContext);

faile:
    avformat_close_input(&inputAVFormatContext);
    /**close output*/
    if(outputAVFormatContext && !(avOutputFormat->flags & AVFMT_NOFILE))
    {
        avio_closep(&outputAVFormatContext->pb);
    }
    avformat_free_context(outputAVFormatContext);

    if(returnCode < 0 && returnCode != AVERROR_EOF)
    {
        logError("Error occurred: %s\n", av_err2str(returnCode));
        return 1;
    }

    return 0;
}





























































